# ESP32-S3 完整版BLE扫描脚本 - 修复名称解码问题
import bluetooth
import time
from micropython import const

# 扫描事件常量
_IRQ_SCAN_RESULT = const(5)
_IRQ_SCAN_DONE = const(6)

class BLEScanner:
    def __init__(self, target_mac=None):
        self.ble = bluetooth.BLE()
        self.ble.active(True)
        self.ble.irq(self._irq_handler)
        self.devices = {}
        self.scanning = False
        self.target_mac = target_mac.upper() if target_mac else None
        self.target_found = False
        
    def _irq_handler(self, event, data):
        """处理BLE事件"""
        if event == _IRQ_SCAN_RESULT:
            # 解析扫描结果
            addr_type, addr, adv_type, rssi, adv_data = data
            
            # 将地址转换为字符串格式
            addr_str = ':'.join(['%02X' % b for b in addr])
            
            # 如果设置了目标MAC，只处理目标设备
            if self.target_mac and addr_str != self.target_mac:
                return
                
            # 解析设备名称
            name = self._decode_name(adv_data)
            if name is None:
                name = "Unknown"
            
            # 存储或更新设备信息
            if addr_str not in self.devices or self.devices[addr_str]['rssi'] != rssi:
                self.devices[addr_str] = {
                    'name': name,
                    'rssi': rssi,
                    'addr_type': addr_type,
                    'last_seen': time.time()
                }
                
                # 如果是目标设备，特别标记
                if self.target_mac and addr_str == self.target_mac:
                    self.target_found = True
                    print(f"★ [目标设备] 名称: {name} | MAC: {addr_str} | 信号: {rssi} dBm")
                else:
                    print(f"  发现设备: {name:<20} | MAC: {addr_str} | 信号: {rssi} dBm")
                    
        elif event == _IRQ_SCAN_DONE:
            self.scanning = False
            
    def _decode_name(self, adv_data):
        """从广播数据中解析设备名称 - 修复版"""
        i = 0
        while i < len(adv_data):
            try:
                length = adv_data[i]
                if length == 0:
                    break
                    
                type_byte = adv_data[i + 1]
                payload = adv_data[i + 2:i + 1 + length]
                
                # 0x09 = Complete Local Name, 0x08 = Shortened Local Name
                if type_byte == 0x09 or type_byte == 0x08:
                    # 尝试多种解码方式
                    # 1. 首先尝试UTF-8
                    try:
                        return payload.decode('utf-8')
                    except:
                        pass
                    
                    # 2. 尝试ASCII
                    try:
                        return payload.decode('ascii')
                    except:
                        pass
                    
                    # 3. 尝试Latin-1
                    try:
                        return payload.decode('latin-1')
                    except:
                        pass
                    
                    # 4. 如果都失败，尝试强制转换可打印字符
                    try:
                        result = ''
                        for b in payload:
                            if 32 <= b < 127:  # 可打印ASCII字符范围
                                result += chr(b)
                            else:
                                result += '?'
                        return result
                    except:
                        # 5. 最后的手段：返回十六进制
                        return "HEX:" + payload.hex()
                        
                i += 1 + length
            except:
                break
        return None
    
    def scan_all(self, duration_ms=10000):
        """扫描所有设备"""
        self.target_mac = None
        print("\n" + "=" * 70)
        print(f"开始扫描所有蓝牙设备 (持续 {duration_ms/1000} 秒)...")
        print("=" * 70)
        
        self.devices.clear()
        self.scanning = True
        self.ble.gap_scan(duration_ms, 30000, 30000, False)
        
        # 等待扫描完成
        while self.scanning:
            time.sleep(0.1)
            
        # 打印汇总
        self.print_summary()
        
    def scan_for_target(self, target_mac, duration_ms=10000):
        """扫描特定MAC地址的设备"""
        self.target_mac = target_mac.upper()
        self.target_found = False
        
        print("\n" + "=" * 70)
        print(f"正在搜索目标设备: {self.target_mac}")
        print(f"扫描时长: {duration_ms/1000} 秒")
        print("=" * 70)
        
        self.devices.clear()
        self.scanning = True
        self.ble.gap_scan(duration_ms, 30000, 30000, False)
        
        # 等待扫描完成
        while self.scanning:
            time.sleep(0.1)
            
        # 显示结果
        if self.target_found:
            print("\n✓ 成功找到目标设备！")
            for addr, info in self.devices.items():
                print(f"  设备名称: {info['name']}")
                print(f"  MAC地址: {addr}")
                print(f"  信号强度: {info['rssi']} dBm")
                print(f"  信号质量: {self._get_signal_quality(info['rssi'])}")
                print(f"  地址类型: {'公共地址' if info['addr_type'] == 0 else '随机地址'}")
        else:
            print(f"\n✗ 未找到目标设备 {self.target_mac}")
            print("  请确认:")
            print("  1. C3设备已开机并运行广播脚本")
            print("  2. MAC地址输入正确")
            print("  3. 设备在信号范围内")
            
    def continuous_monitor(self, target_mac, interval_ms=2000):
        """连续监控目标设备信号强度"""
        self.target_mac = target_mac.upper()
        
        print("\n" + "=" * 70)
        print(f"连续监控模式 - 目标: {self.target_mac}")
        print("按 Ctrl+C 停止监控")
        print("=" * 70)
        
        monitor_count = 0
        
        try:
            while True:
                monitor_count += 1
                self.target_found = False
                self.devices.clear()
                self.scanning = True
                
                # 扫描
                self.ble.gap_scan(interval_ms, 30000, 30000, False)
                
                # 等待扫描完成
                while self.scanning:
                    time.sleep(0.1)
                
                # 显示结果
                if self.target_found:
                    for addr, info in self.devices.items():
                        signal_bar = self._get_signal_bar(info['rssi'])
                        print(f"[{monitor_count:04d}] {info['name']} | RSSI: {info['rssi']:4d} dBm | {signal_bar}")
                else:
                    print(f"[{monitor_count:04d}] 设备离线或超出范围")
                    
                time.sleep(0.5)
                
        except KeyboardInterrupt:
            print("\n停止监控")
            
    def _get_signal_quality(self, rssi):
        """根据RSSI值判断信号质量"""
        if rssi >= -50:
            return "极好 (< 1米)"
        elif rssi >= -60:
            return "良好 (1-2米)"
        elif rssi >= -70:
            return "一般 (2-5米)"
        elif rssi >= -80:
            return "较弱 (5-10米)"
        else:
            return "很弱 (> 10米)"
            
    def _get_signal_bar(self, rssi):
        """生成信号强度条形图"""
        if rssi >= -50:
            return "████████████ 极好"
        elif rssi >= -60:
            return "██████████░░ 良好"
        elif rssi >= -70:
            return "████████░░░░ 一般"
        elif rssi >= -80:
            return "██████░░░░░░ 较弱"
        else:
            return "████░░░░░░░░ 很弱"
            
    def print_summary(self):
        """打印扫描结果汇总"""
        print("\n" + "-" * 70)
        print(f"扫描完成！共发现 {len(self.devices)} 个设备")
        print("-" * 70)
        
        if self.devices:
            # 按信号强度排序
            sorted_devices = sorted(self.devices.items(), 
                                  key=lambda x: x[1]['rssi'], 
                                  reverse=True)
            
            print(f"{'设备名称':<20} | {'MAC地址':<17} | {'信号':<8} | {'质量'}")
            print("-" * 70)
            
            for addr, info in sorted_devices:
                addr_type = "公共" if info['addr_type'] == 0 else "随机"
                print(f"{info['name']:<20} | {addr:<17} | {info['rssi']:<8} | {self._get_signal_quality(info['rssi'])}")
        else:
            print("未发现任何蓝牙设备")
            
    def hex_to_string(self, hex_str):
        """辅助函数：将十六进制字符串转换为ASCII"""
        try:
            hex_str = hex_str.replace(" ", "").replace(":", "")
            return bytes.fromhex(hex_str).decode('ascii')
        except:
            return None
            
    def stop(self):
        """停止扫描并关闭蓝牙"""
        if self.scanning:
            self.ble.gap_scan(None)  # 停止扫描
        self.ble.active(False)
        print("蓝牙已关闭")

# 主程序
def main():
    # ========== 配置区 ==========
    # 在这里设置你的C3设备MAC地址
    TARGET_MAC = "CC:65:78:D8:0A:8E"  # 替换成你的C3实际MAC地址
    # ============================
    
    scanner = BLEScanner()
    
    while True:
        print("\n" + "=" * 70)
        print("ESP32-S3 BLE扫描器 v2.0")
        print("=" * 70)
        print("1. 扫描所有设备")
        print("2. 只扫描目标C3设备")
        print("3. 连续监控目标设备信号强度")
        print("4. 设置新的目标MAC地址")
        print("5. 退出")
        print("-" * 70)
        
        choice = input("请选择功能 (1-5): ").strip()
        
        try:
            if choice == "1":
                # 扫描所有设备
                scanner.scan_all(10000)
                
            elif choice == "2":
                # 扫描特定设备
                print(f"当前目标MAC: {TARGET_MAC}")
                scanner.scan_for_target(TARGET_MAC, 10000)
                
            elif choice == "3":
                # 连续监控
                print(f"当前目标MAC: {TARGET_MAC}")
                scanner.continuous_monitor(TARGET_MAC, 2000)
                
            elif choice == "4":
                # 设置新MAC地址
                print(f"当前目标MAC: {TARGET_MAC}")
                new_mac = input("输入新的MAC地址 (格式 XX:XX:XX:XX:XX:XX): ").strip()
                if len(new_mac) == 17 and new_mac.count(":") == 5:
                    TARGET_MAC = new_mac.upper()
                    print(f"目标MAC已更新为: {TARGET_MAC}")
                else:
                    print("MAC地址格式错误！")
                    
            elif choice == "5":
                print("退出程序...")
                break
                
            else:
                print("无效选择，请重试")
                
        except KeyboardInterrupt:
            print("\n操作被中断")
            continue
        except Exception as e:
            print(f"发生错误: {e}")
            continue
            
        # 询问是否继续
        input("\n按Enter键返回主菜单...")
        
    # 清理资源
    scanner.stop()
    print("程序已退出")

# 自动运行
if __name__ == "__main__":
    main()